package com.gimi.cloud.jimi.gateway.filters.pre;

import com.alibaba.fastjson.JSONObject;
import com.gimi.cloud.jimi.common.enums.ResponseCodeEnum;
import com.gimi.cloud.jimi.common.enums.ServiceNameConstants;
import com.gimi.cloud.jimi.common.enums.common.RedisKeyEnum;
import com.gimi.cloud.jimi.common.jwt.Jwt;
import com.gimi.cloud.jimi.common.utils.IPUtil;
import com.gimi.cloud.jimi.common.utils.JsonUtil;
import com.gimi.cloud.jimi.common.utils.StringUtil;
import com.gimi.cloud.jimi.common.utils.reqhead.Response;
import com.gimi.cloud.jimi.common.utils.reqhead.Util;
import com.gimi.cloud.jimi.dao.redis.RedisServiceImpl;
import com.gimi.cloud.jimi.dto.sys.SysMasterDTO;
import com.gimi.cloud.jimi.gateway.service.CheckMasterService;
import com.gimi.cloud.jimi.gateway.service.CheckTokenService;
import com.gimi.cloud.jimi.gateway.service.ExemptionService;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.nimbusds.jose.JOSEObject;
import com.nimbusds.jose.util.Base64URL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by luck on 2017/09/28.
 * 该过滤器覆盖范围为整个微服务.
 */
@Component
public class PreFilter extends ZuulFilter {
    private static Logger logger = LoggerFactory.getLogger(PreFilter.class);
    private static String[] headerKeys = {"Service-Name", "Authorization"};
    private final ExemptionService exemptionService;
    private final RedisServiceImpl redisService;
    private final CheckTokenService checkTokenService;
    private final CheckMasterService checkMasterService;

    static {
    }

    public PreFilter(ExemptionService exemptionService, RedisServiceImpl redisService,
                     CheckTokenService checkTokenService, CheckMasterService checkMasterService) {
        this.exemptionService = exemptionService;
        this.redisService = redisService;
        this.checkTokenService = checkTokenService;
        this.checkMasterService = checkMasterService;
    }

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        logger.info("===>进入gateway过滤器!");
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        //printRequestContent(request);

        String requestUrl = request.getRequestURL().toString();
        logger.info(String.format("%s request to %s", request.getMethod(), requestUrl));
        Map<String, String> heads = getHeader(request, headerKeys);
        heads = ueditorHeaderHandle(request,heads);
        //此处预留 验签 鉴权 取出jwt token验证(从redis取出该user对应秘钥) 取出token后验证通过,设置相应的user_id到httpRequestHeader里面,方便feign层取出进行调用,注意user_id不从外部传入
        String serviceName = heads.get("Service-Name");
        String token = heads.get("Authorization");
        Response resp = null;

        RequestContext requestContext = RequestContext.getCurrentContext();
        requestContext.addZuulRequestHeader("authorization", heads.get("Authorization"));
        requestContext.addZuulRequestHeader("serviceName", heads.get("Service-Name"));
        requestContext.addZuulRequestHeader("ip", IPUtil.getRemortIP(request));
        requestContext.addZuulRequestHeader("authToken",heads.get("Authorization"));
        requestContext.addZuulRequestHeader("url",request.getRequestURL().toString());

        String serviceType = getServiceType(requestContext, requestUrl);
        switch (serviceType) {
            case "manage":
                //token = "eyJhbGciOiJIUzI1NiJ9.eyJleHQiOjE1MTU3Nzc2NTI1MDAsInVzZXJOYW1lIjoiYWRtaW4iLCJ1c2VySWQiOjF9.jpeuIaMDvXgflSF4RL2HbU7Rrb7axA6ANTbmajocBEM";
                requestContext = manageFilter(requestContext, token, serviceName);
                break;
            case "merchant":
                //token = "eyJhbGciOiJIUzI1NiJ9.eyJleHQiOjE1MTU3Nzc2NTI1MDAsInVzZXJOYW1lIjoiYWRtaW4iLCJ1c2VySWQiOjF9.jpeuIaMDvXgflSF4RL2HbU7Rrb7axA6ANTbmajocBEM";
                requestContext = manageFilter(requestContext, token,serviceName);
                break;
            case "app":
                requestContext = appFilter(requestContext, token, resp, serviceName);
                break;
            default:
                break;
        }

        logger.info("token校验完成\n");


        return null;
    }

    private RequestContext manageFilter(RequestContext requestContext, String token, String serviceName) {
        //checkToken
        //登录路径和Ueditor操作路径不校验token
        String[] notCheck = {ServiceNameConstants.MASTER_LOGIN, ServiceNameConstants.UEDITOR_ACTION};
        if(!Arrays.asList(notCheck).contains(serviceName)){
            ResponseCodeEnum checkToken = checkTokenService.checkToken(token, "manage");
            if (checkToken != null) {
                sendResponse(requestContext, checkToken);
                return null;
            }
            //init baseDTO
            Map payload = getPayload(token);
            String userName = (String) payload.get("userName");
            SysMasterDTO masterDTO = checkMasterService.getMasterByUserName(userName);
            if (masterDTO == null) {
                sendResponse(requestContext, ResponseCodeEnum.ERR_USER_INVALID);
                return null;
            }
            String maId = masterDTO.getMaId() + "";
            String username = masterDTO.getUsername();
            DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String time = df.format(new Date());
            requestContext.addZuulRequestHeader("uid", maId);
            requestContext.addZuulRequestHeader("uname", username);
            requestContext.addZuulRequestHeader("addUid", maId);
            requestContext.addZuulRequestHeader("addUname", username);
            requestContext.addZuulRequestHeader("addTime", time);
            requestContext.addZuulRequestHeader("updateUid", maId);
            requestContext.addZuulRequestHeader("updateUname", username);
            requestContext.addZuulRequestHeader("updateTime", time);
        }

        return requestContext;
    }

    /**
     * app的接口过滤方法
     *
     * @param requestContext
     * @param token
     * @return
     */
    private RequestContext appFilter(RequestContext requestContext, String token, Response resp, String serviceName) {
        if (StringUtil.isBlank(serviceName)) {
            logger.info(ResponseCodeEnum.SERVICE_NAME_NOT_FOUND.getDesc());
            sendResponse(requestContext, ResponseCodeEnum.SERVICE_NAME_NOT_FOUND);
            return null;
        }
        String userId = "0";
        Map exemptionMap = exemptionService.getExemptionMap();
        if (!exemptionMap.containsKey(serviceName)) {
            //String secret = null;
            //secret = redisService.get("jwt_token_secret");
            resp = new Response();
            ResponseCodeEnum codeEnum = checkTokenService.checkToken(token, "app");
            if (codeEnum != null) {
                resp.setResponseCode(codeEnum);
                requestContext.setSendZuulResponse(false);
                requestContext.setResponseStatusCode(200);
                Util.sendResponse(JSONObject.toJSONString(resp), requestContext.getResponse());
                return null;
            }
            userId = getUserId(token);
            if (userId == null || userId.equals("0")) {
                sendResponse(requestContext, ResponseCodeEnum.SIGNATURE_ERROR);
                return null;
            }
            String redisToken = redisService.get(RedisKeyEnum.JIMI_APP_PRE.getKey() + "user:tokenStore:userId_" + userId);
            if (!StringUtil.isBlank(redisToken) && !token.equals(redisToken)) {
                sendResponse(requestContext, ResponseCodeEnum.LOGIN_REPEAT_ERROR);
                return null;
            }

        }

        requestContext.addZuulRequestHeader("userId", userId);//设置header示例
        return requestContext;
    }

    private String getServiceType(RequestContext context, String requestUrl) {
        if (StringUtil.isBlank(requestUrl)) {
            sendResponse(context, ResponseCodeEnum.SERVICE_NAME_NOT_FOUND);
        }
        String[] arr = requestUrl.split("/");
        if (arr == null || arr.length < 2) {
            sendResponse(context, ResponseCodeEnum.SERVICE_NAME_NOT_FOUND);
        }
        return arr[arr.length - 2];
    }

    /**
     * 获取header信息
     *
     * @param req  request请求
     * @param keys 键集合
     * @return value
     */
    private Map<String, String> getHeader(HttpServletRequest req, String[] keys) {
        Map<String, String> headerMap = new HashMap<>();
        for (String key : keys) {
            if (key.equals("Authorization")) {
                String tokenStr = req.getHeader(key);
                if (tokenStr != null && tokenStr.length() > 7) {
                    tokenStr = tokenStr.substring(7, tokenStr.length());
                }
                headerMap.put(key, tokenStr);
            } else {
                headerMap.put(key, req.getHeader(key));
            }
        }
        return headerMap;
    }

    /**
     * 处理Ueditor的参数操作
     * eg：http://localhost:8775/jimi/merchant/service?Authorization=common&Service-Name=1&handleClassName=productUeditorAction
     * Authorization为common生成一个Authorization，将Service-Name放入headerMap
     * @param headerMap
     * @return
     */
    private Map<String,String> ueditorHeaderHandle(HttpServletRequest req,Map<String,String> headerMap){
        String authKey = "Authorization";
        String serviceNameKey = "Service-Name";

        if("common".equals(req.getParameter(authKey))){
            Map<String,Object> payload1 = new HashMap<>();
            payload1.put("maId",1);
            payload1.put("userName","admin");
            payload1.put("ext", new Date().getTime() + 1000 * 60 * 60 * 24 * 60l);//过期时间60天
            String tokenStr = Jwt.createToken(payload1,Jwt.getSecret());
            headerMap.put(authKey, tokenStr);

            headerMap.put(serviceNameKey, req.getParameter(serviceNameKey));
        }
        return headerMap;
    }


    private String getUserId(String token) {
        String result = "0";
        try {
            Base64URL second = JOSEObject.split(token)[1];
            String payload = new String(Base64.getDecoder().decode(second.toString()), "utf-8");
            result = JSONObject.parseObject(payload).getString("userId");
        } catch (Exception e) {
            logger.error("获取userId异常：" + e.getMessage());
        }
        return result;
    }

    private Map getPayload(String token) {
        String result = "{}";
        try {
            Base64URL second = JOSEObject.split(token)[1];
            result = new String(Base64.getDecoder().decode(second.toString()), "utf-8");
        } catch (Exception e) {
            logger.error("获取userId异常：" + e.getMessage());
        }
        return JsonUtil.jsonToMap(result);
    }

    private void sendResponse(RequestContext ctx, ResponseCodeEnum status) {
        Response resp = new Response();
        ctx.setSendZuulResponse(false);
        ctx.setResponseStatusCode(200);
        resp.setResponseCode(status);
        Util.sendResponse(JSONObject.toJSONString(resp), ctx.getResponse());
    }

    private void printRequestContent(HttpServletRequest request){
        try {
            BufferedReader br = request.getReader();
            String str, wholeStr = "";
            while((str = br.readLine()) != null){
                wholeStr += str;
            }
            System.out.println(wholeStr);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
